
前一篇認識了 Node.js 是什麼,也知道 JavaScript 不只能在瀏覽器裡執行。那麼,Node.js 到底要怎麼拿來開發後端呢?
這篇就從最基本的 Server 開始,不使用框架,直接透過 Node.js 內建的 http 模組,建立第一台 Web Server。
Server 中文通常翻譯成「伺服器」。第一次看到這個詞時,可能會想到機房裡一台台的大型電腦,但在 Web 開發中,我們談到 Server 時,不一定是在指某一台實體機器,也可能是在說一個「接收請求、處理請求,再回傳結果的程式」。
例如在瀏覽器輸入網址時,瀏覽器會向 Server 發出請求。Server 收到請求後進行處理,再把結果傳回瀏覽器。
而這正是接下來要使用 Node.js 完成的事情。
在 Web 世界中,經常會看到兩個角色:Client(客戶端)和 Server(伺服器端)。
Client 是發出請求的一方,最常見的 Client 就是瀏覽器,例如 Chrome、Safari、Firefox。
當我們打開一個網站時,瀏覽器會向 Server 發出請求,Server 收到後可能會讀取資料、進行運算,再把結果回傳給瀏覽器。
整個流程可以先簡單理解成:
Client → 發出請求 → Server
Client ← 回傳結果 ← Server(注意這邊的箭頭是反過來的)
不過 Client 不一定是瀏覽器,手機 App、其他 Server,甚至之後會使用到的 API 測試工具,都可以扮演 Client 的角色。
知道 Client 和 Server 的角色之後,接著來看它們實際傳遞的是什麼。
Client 和 Server 之間的溝通,可以拆成兩個重要概念:Request(請求)與 Response(回應)。
Request 是 Client 傳送給 Server 的訊息,用來告訴 Server 想要做什麼;Response 則是 Server 處理完 Request 後,將結果回傳給 Client。
一個 Request 可以先認識四個主要部分:Method、URL、Headers 和 Body。
Request
├─ Method → 想做什麼
├─ URL → 想存取哪個位置
├─ Headers → 請求的額外資訊
└─ Body → 傳送的資料
例如想新增一筆商品時,可以想像 Client 傳送了這些資訊:
Method:POST
URL:/products
Headers:Content-Type: application/json
Body:
{
"name": "Keyboard",
"price": 1200
}
其中 POST 是 Method,表示這次想新增資料,/products 是 URL,表示要存取的位置,Content-Type 是 Header,告訴 Server 傳送的資料格式是 JSON,最後的 JSON 則是 Body,也就是真正要傳給 Server 的商品資料。
常見的 Method 可以先認識這幾種:
GET → 取得資料
POST → 新增資料
PATCH → 修改資料
DELETE → 刪除資料
並不是每個 Request 都會有 Body。例如想取得商品資料時,可以發出:
GET /products
這代表 Client 想取得 /products 的資料,通常不需要另外傳送 Body。
Server 收到 Request 並處理完成後,就會將 Response 傳回 Client。一個 Response 可以先認識三個主要部分:Status Code、Headers 和 Body。
Response
├─ Status Code → 處理結果
├─ Headers → 回應的額外資訊
└─ Body → 回傳的資料
例如成功取得商品後,Server 可能會回傳:
Status Code:200
Headers:Content-Type: application/json
Body:
{
"name": "Keyboard",
"price": 1200
}
其中 200 是 Status Code,表示這次請求成功,Content-Type 是 Header,告訴 Client 回傳的資料格式是 JSON,最後的 JSON 則是 Body,也就是真正回傳的商品資料。
Status Code 有很多種,目前可以先認識幾個常見的:
200 → 請求成功
201 → 成功新增資料
400 → 請求內容有問題
404 → 找不到資料
500 → Server 發生錯誤
所以可以先記住:Request 是 Client 告訴 Server「我想做什麼」,Response 則是 Server 告訴 Client「這是處理結果」。
接下來,我們就實際使用 Node.js 把這個流程做出來。
Node.js 本身提供了 http 模組,可以用來建立 HTTP Server,因此目前還不需要安裝 Express。
首先建立一個 JavaScript 檔案,例如 server.js,接著載入 Node.js 內建的 http 模組:
// 載入 Node.js 內建的 http 模組
const http = require("http");
因為 http 是 Node.js 的內建模組,所以不需要另外執行 npm install。
接著使用 http.createServer() 建立 Server:
// 建立 HTTP Server
const server = http.createServer(function (req, res) {
// req:Client 傳進來的 Request
// res:Server 準備回傳的 Response
});
createServer() 接收一個 callback function。每當 Server 收到 Client 傳來的 HTTP Request 時,這個 function 就會被執行。
這裡有兩個很重要的參數:req 和 res。req 代表 Request,也就是 Client 傳進來的請求,res 代表 Response,也就是 Server 準備傳回 Client 的回應。
這其實就是前面介紹的 Request / Response,只是到了 Node.js 裡,我們會透過 req 和 res 這兩個物件來操作它們。
Server 建立完成後,還需要告訴它要在哪一個 Port 等待 Request,例如:
// 監聽本機的 3000 Port
server.listen(3000);
這裡的 3000 就是 Server 使用的 Port,可以先把 Port 想像成 Server 對外提供服務的「入口」。當 Client 透過這個 Port 發出 Request 時,Server 就可以接收到請求。
把目前的程式組合起來:
// 載入 Node.js 內建的 http 模組
const http = require("http");
// 建立 HTTP Server
const server = http.createServer(function (req, res) {
// 收到 Request 後會執行這裡的程式
});
// 監聽本機的 3000 Port
server.listen(3000);
接著在終端機執行:
node server.js
Server 就會開始運作。這時如果打開瀏覽器並進入:
http://127.0.0.1:3000/
瀏覽器會向 Server 發出 Request,而剛才設定的 callback function 也會被執行。不過目前 function 裡沒有任何處理 Response 的程式,也沒有使用 res.end() 告訴 Client「這次 Response 已經結束」。
因此瀏覽器可能會一直顯示載入中的狀態,等待 Server 回傳完整的 Response。
瀏覽器發出 Request
↓
Server 收到 Request
↓
callback function 執行
↓
沒有結束 Response
↓
瀏覽器繼續等待
所以建立 Server 並使用 listen() 開始監聽,只代表 Server 已經可以「接收 Request」,接下來還需要透過 res 建立並結束 Response,Client 才能知道這次請求已經處理完成。
下一步,就來讓 Server 回傳第一段內容。
接下來讓 Server 收到 Request 後,回傳一段 Hello Node.js!:
const http = require("http");
const server = http.createServer(function (req, res) {
// 將內容寫入 Response Body
res.write("Hello Node.js!");
// 結束這次 Response
res.end();
});
server.listen(3000);
在終端機執行:
node server.js
再打開瀏覽器:
http://127.0.0.1:3000/
就可以看到:
Hello Node.js!
這裡使用了兩個新的方法:res.write() 和 res.end()。
res.write():寫入要回傳的內容res.write() 用來將資料寫入 Response Body,也就是 Client 最後會收到的內容:
// 將文字寫入 Response Body
res.write("Hello Node.js!");
res.write() 也可以呼叫多次,每次寫入的資料都會成為同一次 Response 的內容:
// 分兩次寫入 Response Body
res.write("Hello ");
res.write("Node.js!");
// 結束 Response
res.end();
Client 最後會收到:
Hello Node.js!
res.end():結束這次回應資料傳送完成後,需要使用 res.end() 結束這次 Response:
// 告訴 Server:這次 Response 已經完成
res.end();
如果只有簡單的一段資料,也可以直接將內容放進 res.end():
// 傳送內容並結束 Response
res.end("Hello Node.js!");
因此下面兩種寫法都可以:
// 先寫入內容,再結束 Response
res.write("Hello Node.js!");
res.end();
// 一次完成傳送內容與結束 Response
res.end("Hello Node.js!");
目前為了看清楚「寫入內容」和「結束回應」兩個步驟,後面的範例仍會使用 res.write() 搭配 res.end()。
剛才使用 res 把資料傳回 Client,接下來換個方向,看看 Client 傳了什麼資料給 Server。前面提過,req 代表 Request,因此可以從 req 取得 Client 傳進來的資訊。
例如:
const server = http.createServer(function (req, res) {
// 取得請求的 URL
console.log(req.url);
// 取得 HTTP Method
console.log(req.method);
// 取得 Request Headers
console.log(req.headers);
res.end("Hello Node.js!");
});
其中 req.url 可以取得這次請求的 URL,req.method 可以知道這次使用的是 GET、POST、DELETE 等哪一種 HTTP Method,而 req.headers 則可以取得 Request 帶進來的 Headers。
目前先從最容易觀察的 req.url 開始。
如果使用者進入:
http://127.0.0.1:3000/
req.url 會取得:
/
如果進入:
http://127.0.0.1:3000/products
req.url 則會取得:
/products
因此可以根據 req.url 判斷 Client 請求了哪個網址,再回傳不同的內容:
const http = require("http");
const server = http.createServer(function (req, res) {
// 進入首頁
if (req.url === "/") {
res.write("Home");
}
// 進入商品頁
else if (req.url === "/products") {
res.write("Products");
}
// 結束 Response
res.end();
});
server.listen(3000);
現在進入首頁會看到 Home,進入 /products 則會看到 Products。
這其實已經開始接觸到「路由」的概念:根據不同的 URL,決定 Server 要進行什麼處理。之後使用 Express 時,會有更方便的方式處理這件事。
目前 Server 回傳的都是文字,但實際開發 API 時,更常見的是回傳 JSON,例如:
{
"message": "Hello Node.js!"
}
我們可以先建立一個 JavaScript 物件,再使用 JSON.stringify() 將它轉換成 JSON 字串:
// 建立要回傳的資料
const data = {
message: "Hello Node.js!"
};
// 將 JavaScript 物件轉成 JSON 字串
const jsonData = JSON.stringify(data);
接著使用剛才學到的 res.write() 將資料寫入 Response Body。不過在回傳 JSON 前,還需要告訴 Client 這次回傳的資料格式,因此會使用 res.writeHead():
// 設定 Status Code 和 Response Header
res.writeHead(200, {
"Content-Type": "application/json"
});
// 寫入 JSON 資料
res.write(jsonData);
// 結束 Response
res.end();
其中 200 是 HTTP Status Code,代表這次 Request 成功,而:
"Content-Type": "application/json"
是一種 HTTP Header,用來告訴 Client:「這次 Response Body 的資料格式是 JSON。」
把前面的內容組合起來:
const http = require("http");
const server = http.createServer(function (req, res) {
// 準備要回傳的資料
const data = {
message: "Hello Node.js!"
};
// 設定狀態碼與回傳資料的格式
res.writeHead(200, {
"Content-Type": "application/json"
});
// 將物件轉成 JSON 字串並寫入 Response Body
res.write(JSON.stringify(data));
// 結束 Response
res.end();
});
// 啟動 Server
server.listen(3000);
現在再開啟:
http://127.0.0.1:3000/
就可以收到 Server 回傳的 JSON 資料。
到這裡,也可以把三個方法放在一起理解:
res.writeHead(); // 設定 Status Code 和 Headers
res.write(); // 寫入 Response Body
res.end(); // 結束 Response
它們分別對應到前面介紹過的 Response 結構:
Response
├─ Status Code → 200
├─ Headers → Content-Type: application/json
└─ Body → {"message":"Hello Node.js!"}
這篇先不用急著記住所有 Header,只要知道 Content-Type 可以告訴 Client 回傳的資料格式即可。之後實際串接前後端 API 時,再進一步認識其他 Header 和 CORS。
這篇使用 Node.js 原生的 http 模組,建立了第一台 Web Server,也實際完成了一次 Client 和 Server 之間的 Request / Response。
整個流程可以整理如下:

在 Node.js 裡,可以透過 req 讀取 Client 傳來的 Request,例如 req.url、req.method 和 req.headers。
而 res 則負責建立並送出 Response,例如使用 res.writeHead() 設定回應資訊、res.write() 寫入內容,以及 res.end() 結束回應。
目前使用原生 http 模組時,還需要自己判斷 req.url、設定 Header,以及處理 Response。雖然寫起來有些繁瑣,但也能幫助我們先理解 Server 背後實際做了哪些事情。
之後開始使用 Express,就會發現這些原本需要自己處理的流程,可以用更簡潔的方式完成。